package com.lagou.edu.factory;

import com.lagou.edu.dao.annotation.*;
import javafx.beans.binding.ObjectBinding;
import org.dom4j.Comment;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
@Service("beanFactory")
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String, Object> map = new HashMap<>();  // 存储对象


    static {
        //扫描包下的所有注解带注解的类
        try {
            getClasses("com.lagou.edu");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return map.get(id);
    }

    private static void getClasses(String packageName) throws IOException, IllegalAccessException {

        Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/"));//拿到根包路径
        while (resources.hasMoreElements()) {
            URL url = resources.nextElement();
            if ("file".equals(url.getProtocol())) {
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");///Users/april/Desktop/Java_L2_2019.12.23/02_讲义/第一阶段/spring/代码/lagou-transfer/target/classes/com/lagou/dedu
                //处理当前包下的所有类
                initMap(packageName, filePath);
            }

        }
        //map 初始化完毕之后,遍历对象,给 filed 设置值
        ProxyFactory o1 = (ProxyFactory) map.get(ProxyFactory.class.getName());
        for (String s : map.keySet()) {
            //看当前类是否有@transactional 注解,如果有的话,将 map 中存的值替换为代理对象
            Object o = map.get(s);
            if(o.getClass().isAnnotationPresent(Transactional.class)){
                map.put(s,o1.getJdkProxy(o));
            }
            Field[] fields = o.getClass().getDeclaredFields();
            for (Field field : fields) {
                //设置暴力访问
                field.setAccessible(true);
                if (field.getAnnotation(Autowired.class) != null) {
                    //按照类型注入
                    field.set(o, map.get(field.getType().getName()));
                }
            }

        }

    }

    private static void initMap(String packageName, String packagePath) {
        // 获取此包的目录 建立一个File
        File dir = new File(packagePath);
        // 如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
            return;
        }
        // 如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            public boolean accept(File file) {
                return (file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        for (File file : dirfiles) {
            // 如果是目录 则继续扫描
            if (file.isDirectory()) {
                initMap(packageName + "." + file.getName(), file.getAbsolutePath());
            } else {
                // 如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    // 添加到集合中去
                    //将map 的 id 拿到(类名首字母小写)
                    if (!className.equals("TransferServlet")) {//Controller层的类不解析
                        Class<?> aClass = Class.forName(packageName + '.' + className);
                        Object o = aClass.newInstance();
                        map.put(aClass.getName(), o);
                        //如果有加 id 注解,给id+value put 进 map 一条数据
                        Component component = aClass.getAnnotation(Component.class);
                        if (component != null) {
                            if (component.value() != null && component.value() != "") {
                                map.put(component.value(), o);
                            } else {
                                map.put(lowerFirstLetter(className), o);
                            }
                        }

                        Service service = aClass.getAnnotation(Service.class);
                        if (service != null) {
                            if (service.value() != null && service.value() != "") {
                                map.put(service.value(), o);
                            } else {
                                map.put(lowerFirstLetter(className), o);
                            }
                        }

                        Repository repository = aClass.getAnnotation(Repository.class);
                        if (repository != null) {
                            if (repository.value() != null && repository.value() != "") {
                                map.put(repository.value(), o);
                            } else {
                                map.put(lowerFirstLetter(className), o);
                            }
                        }

                        Class<?>[] interfaces = aClass.getInterfaces();
                        for (Class<?> anInterface : interfaces) {
                            map.put(anInterface.getName(), o);//全路径名,方便 autowired
                        }

                    }
                } catch (ClassNotFoundException e) {
                    // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 类名第一个字母小写
     * @param str
     * @return
     */
    private static String lowerFirstLetter(String str) {
        char lowerFirst = (char) (str.charAt(0) + 32);
        String result = lowerFirst + str.substring(1);
        return result;

    }


}
